package customSpring;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.ConvertUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

/**
 * 
 */
public class ItcastClassPathXMLApplicationContext {
    private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
    private Map<String, Object>  sigletons   = new HashMap<String, Object>();

    public ItcastClassPathXMLApplicationContext(String filename) {
        this.readXML(filename);
        this.instanceBeans();
        this.annotationInject(); //专门处理注解方式
        this.injectObject();
    }

    /**
     * 
     */
    private void annotationInject() {
        for (String beanName : sigletons.keySet()) {//循环所有的bean对象
            Object bean = sigletons.get(beanName);
            if (bean != null) {//获取到bean对象后就判断下bean对象是否存在
                try {
                    //对属性进行处理
                    PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass())
                        .getPropertyDescriptors();


                    //获取bean的属性  该部分针对注解的set方法注入
                    for (PropertyDescriptor properdesc : ps) {//for循环属性描述
                        Method setter = properdesc.getWriteMethod();//获取属性的setter方法
                        if (setter != null && setter.isAnnotationPresent(Autowire.class)) {
                            Autowire resource = setter.getAnnotation(Autowire.class);
                            Object value = null;
                            if (resource.name() != null && !"".equals(resource.name())) {
                                value = sigletons.get(resource.name());
                            } else {
                                value = sigletons.get(properdesc.getName());
                                if (value == null) {//当找不到与名称匹配的bean会按类型去寻找
                                    for (String key : sigletons.keySet()) {//for循环所有的bean对象
                                        if (properdesc.getPropertyType().isAssignableFrom(
                                            sigletons.get(key).getClass())) {
                                            //判断属性的类型是否和bean想匹配
                                            //isAssignableFrom这个方法判断properdesc.getPropertyType()这个类型是否是
                                            //sigletons.get(key).getClass()的接口或者父类，或者是它类的本身
                                            value = sigletons.get(key);
                                            break;
                                        }
                                    }
                                }
                            }
                            setter.setAccessible(true);
                            setter.invoke(bean, value);//把引用对象注入属性
                        }
                    }
                    
                    //对字段进行处理 该部分针对注解的字段注入
                    Field[] fields = bean.getClass().getDeclaredFields();
                    //找到申明的所有字段
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(Autowire.class)) {
                            Autowire resource = field.getAnnotation(Autowire.class);
                            Object value = null;
                            if (resource.name() != null && !"".equals(resource.name())) {
                                value = sigletons.get(resource.name());
                            } else {
                                value = sigletons.get(field.getName());
                                if (value == null) {
                                    for (String key : sigletons.keySet()) {
                                        if (field.getType().isAssignableFrom(
                                            sigletons.get(key).getClass())) {
                                            value = sigletons.get(key);
                                            break;
                                        }
                                    }
                                }
                            }
                            field.setAccessible(true);//设置允许访问private字段
                            field.set(bean, value);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 为bean对象的属性注入值
     */
    private void injectObject() {
        for (BeanDefinition beanDefinition : beanDefines) {
            Object bean = sigletons.get(beanDefinition.getId());
            if (bean != null) {
                try {
                    PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass())
                        .getPropertyDescriptors();
                    //Introspector通过这个类可以取得bean的定义信息

                    for (PropertyDefinition propertyDefinition : beanDefinition.getPropertys()) {

                        for (PropertyDescriptor properdesc : ps) {

                            if (propertyDefinition.getName().equals(properdesc.getName())) {

                                Method setter = properdesc.getWriteMethod();// 获取属性的setter方法,private

                                if (setter != null) {//属性可能没有set方法，所以这里要判断一下
                                    Object value = null;
                                    if (propertyDefinition.getRef() != null
                                        && !"".equals(propertyDefinition.getRef().trim())) {
                                        value = sigletons.get(propertyDefinition.getRef());
                                    } else {
                                        value = ConvertUtils.convert(propertyDefinition.getValue(),
                                            properdesc.getPropertyType());
                                        /*
                                         我们怎么将本身是字符串的值转成相应的属性类型的值呢？
                                         这时我们可以使用一个叫beanutils的工具，是Apache开源组织给我们提供的，使用它的话，可以很容易的
                                         把字符串类型的值转换成我需要的属性类型的值
                                        */
                                    }
                                    setter.setAccessible(true);//如果set方法是私有的话,要设置它允许被访问
                                    setter.invoke(bean, value);// 把引用对象注入到属性
                                }
                                break;
                            }
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
    }

    /**
     * 实现bean的实例化
     */
    private void instanceBeans() {
        for (BeanDefinition beanDefinition : beanDefines) {
            try {
                if (beanDefinition.getClassName() != null
                    && !"".equals(beanDefinition.getClassName().trim()))
                    sigletons.put(beanDefinition.getId(),
                        Class.forName(beanDefinition.getClassName()).newInstance());
            } catch (Exception e) {
                // 通过反射技术把bean都创建出来
                e.printStackTrace();
            }
        }
    }

    /**
     * 读取xml配置文件
     */
    @SuppressWarnings("unchecked")
    private void readXML(String filename) {
        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            URL xmlpath = this.getClass().getClassLoader().getResource(filename);
            document = saxReader.read(xmlpath);
            Map<String, String> nsMap = new HashMap<String, String>();
            nsMap.put("ns", "http://www.springframework.org/schema/beans");// 加入命名空间
            XPath xsub = document.createXPath("//ns:beans/ns:bean");// 创建beans/bean查询路径
            xsub.setNamespaceURIs(nsMap);// 设置命名空间
            List<Element> beans = xsub.selectNodes(document);// 获取文档下所有bean节点
            for (Element element : beans) {
                String id = element.attributeValue("id");// 获取id属性值
                String clazz = element.attributeValue("class"); // 获取class属性值
                BeanDefinition beanDefine = new BeanDefinition(id, clazz);
                XPath propertysub = element.createXPath("ns:property");
                propertysub.setNamespaceURIs(nsMap);// 设置命名空间
                List<Element> propertys = propertysub.selectNodes(element);
                for (Element property : propertys) {
                    String propertyName = property.attributeValue("name");
                    String propertyref = property.attributeValue("ref");
                    String propertyValue = property.attributeValue("value");
                    PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName,
                        propertyref, propertyValue);
                    beanDefine.getPropertys().add(propertyDefinition);
                }
                beanDefines.add(beanDefine);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取bean实例
     */
    public Object getBean(String beanName) {
        return this.sigletons.get(beanName);
    }
}
